/*
  This file is a part of Awesomium, a library that makes it easy for 
  developers to embed web-content in their applications.

  Copyright (C) 2009 Adam J. Simmons

  Project Website:
  <http://princeofcode.com/awesomium.php>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
  02110-1301 USA
*/

#include "WebView/RenderBuffer.h"

#include <string.h>
#include <assert.h>
#include "gfx/rect.h"

using namespace Awesomium;

namespace {

void copyBuffers(int height, unsigned char* src, int srcRowSpan, unsigned char* dest, int destRowSpan, bool convertToRGBA)
{
  if(!convertToRGBA)
  {
    if (destRowSpan == srcRowSpan)
    {
      size_t buffer_size = height * srcRowSpan;
      memcpy(dest, src, buffer_size);
    }
    else
    {
      for(int row = 0; row < height; row++)
        memcpy(dest + row * destRowSpan, src + row * srcRowSpan, srcRowSpan);
    }
  }
  else
  {
    // this is the _slow_ path, shouldn't be used, or should be optimized

    int srcRowOffset, destRowOffset;
    for(int row = 0; row < height; row++)
    {
      srcRowOffset = row * srcRowSpan;
      destRowOffset = row * destRowSpan;

      for(int colOffset = 0; colOffset < srcRowSpan; colOffset += 4)
      {
        dest[destRowOffset + colOffset] = src[srcRowOffset + colOffset + 2];
        dest[destRowOffset + colOffset + 1] = src[srcRowOffset + colOffset + 1];
        dest[destRowOffset + colOffset + 2] = src[srcRowOffset + colOffset];
        dest[destRowOffset + colOffset + 3] = src[srcRowOffset + colOffset + 3];
      }
    }
  }
}

void setOpaqueAlpha(int x, int y, int width, int height, unsigned char* buffer, int bufferRowSpan)
{
  unsigned char* row_ptr = buffer + (y * bufferRowSpan) + (x * 4);

  for(int row = 0; row < height; row++)
  {
    unsigned char* alpha_ptr = row_ptr + 3;

    for(int col = 0; col < width; col++)
    {
      *alpha_ptr = 255;
      alpha_ptr += 4;
    }

    row_ptr += bufferRowSpan;
  }
}

}

RenderBuffer::RenderBuffer(int width_, int height_) : buffer(0), width(width_), height(height_), rowSpan(width_*4)
{
  reserve(width, height);
}

RenderBuffer::~RenderBuffer()
{
  if(buffer)
    delete[] buffer;
}

void RenderBuffer::reserve(int width, int height)
{
  delete [] buffer;

  buffer = new unsigned char[width * height * 4];
  memset(buffer, 255, width * height * 4);
}

void RenderBuffer::copyBufferFrom(const RenderBuffer& src)
{
  assert(width == src.width && height == src.height);
  memcpy(buffer, src.buffer, width*height*4);
}

void RenderBuffer::copyAreaFrom(const RenderBuffer& buffer, const gfx::Rect& srcRect)
{
  assert(width == buffer.width && height == buffer.height);

  gfx::Rect intersect = gfx::Rect(width, height).Intersect(srcRect);
  if(intersect.IsEmpty())
    return;

  copyAreaFrom( 
    intersect
  , buffer.buffer + (intersect.y() * buffer.rowSpan) + (intersect.x() * 4)
  , buffer.rowSpan
  );
}

void RenderBuffer::copyAreaFrom(const gfx::Rect& dstRect, unsigned char* srcBuffer, int srcRowSpan, bool forceOpaque)
{
  if(gfx::Rect(width, height).Contains(dstRect))
  {
    // no clipping

    int src_height = dstRect.height();
    int src_width  = dstRect.width();

    unsigned char* src_row = srcBuffer;
    unsigned char* dst_row = buffer + (dstRect.y() * rowSpan) + (dstRect.x() * 4);

    for(int row = 0; row < src_height; row++)
    {
      memcpy(dst_row, src_row, src_width * 4);
      src_row += srcRowSpan;
      dst_row += rowSpan;
    }

    if(forceOpaque)
    {
      setOpaqueAlpha(dstRect.x(), dstRect.y(), src_width, src_height, buffer, rowSpan);
    }
  }
  else
  {
    gfx::Rect intersect = gfx::Rect(width, height).Intersect(dstRect);
    if(intersect.IsEmpty())
      return;

    int srcOffsetX = intersect.x() - dstRect.x();
    int srcOffsetY = intersect.y() - dstRect.y();

    int copy_height = intersect.height();
    int copy_width = intersect.width();

    unsigned char* src_row = srcBuffer + (srcOffsetY * srcRowSpan) + (srcOffsetX * 4);
    unsigned char* dst_row = buffer + (intersect.y() * rowSpan) + (intersect.x() * 4);

    for(int row = 0; row < copy_height; row++)
    {
      memcpy(dst_row, src_row, copy_width * 4);
      src_row += srcRowSpan;
      dst_row += rowSpan;
    }

    if(forceOpaque)
    {
      setOpaqueAlpha(intersect.x(), intersect.y(), copy_width, copy_height, buffer, rowSpan);
    }
  }
}

void RenderBuffer::copyTo(unsigned char* destBuffer, int destRowSpan, bool convertToRGBA)
{
  copyBuffers(height, buffer, width * 4, destBuffer, destRowSpan, convertToRGBA);
}

void RenderBuffer::copyAreaTo(const gfx::Rect& srcRect, int destX, int destY, unsigned char* destBuffer, int destRowSpan, bool convertToRGBA)
{
  gfx::Rect intersect = gfx::Rect(width, height).Intersect(srcRect);
  if(intersect.IsEmpty())
    return;

  int copy_height = intersect.height();
  int copy_width = intersect.width();

  unsigned char* src_row = buffer + (intersect.y() * rowSpan) + (intersect.x() * 4);
  unsigned char* dst_row = destBuffer + (destY * destRowSpan) + (destX * 4);

  for(int row = 0; row < copy_height; row++)
  {
    memcpy(dst_row, src_row, copy_width * 4);
    src_row += rowSpan;
    dst_row += destRowSpan;
  }
}
